# RAG Architecture (Retrieval-Augmented Generation)

This document describes in detail the implemented RAG system architecture, covering the ingestion (creation) and retrieval (search) pipelines, as well as all technical configurations.

## 1. Overview

The RAG system has been modernized to a modular architecture located in `src/rag/`. It is composed of decoupled services for Embedding, Indexing, and Retrieval.

### Key Components

*   **Embedder**: Responsible for converting text into vectors.
*   **VectorIndex**: Manages the FAISS index and persistent metadata.
*   **Retriever**: Search coordinator that unites Embedder and VectorIndex with filtering logic.
*   **Tools**: Interface for Agents (LangChain), providing structured JSON output.

---

## 2. Ingestion Pipeline (Vector DB Creation)

This flow occurs when documents (e.g., uploaded PDFs) are processed and added to the knowledge base.
**Script**: `src/rag/generate_vector_db.py`

### Steps:
1.  **Text Extraction**: `PyPDF2` extracts raw text from files.
2.  **Chunking**: The text is broken into smaller pieces to maintain relevant context.
    *   **Strategy**: `Recursive Character Chunking` (split by paragraphs, then characters).
    *   **Chunk Size**: `1000` characters (approximately 200-250 tokens).
    *   **Overlap**: `100` characters (to preserve context between cuts).
3.  **Embedding**: Conversion of chunks into dense vectors.
    *   **Model**: `sentence-transformers/all-MiniLM-L6-v2`.
    *   **Dimension**: `384` dimensions.
    *   **Normalization**: `L2 Normalization` (unit length) applied to all vectors to allow search by Cosine Similarity via inner product.
4.  **Indexing (FAISS)**: Storage of vectors.
    *   **Index Type**: `IndexIDMap` wrapping an `IndexFlatIP`.
    *   **Metric**: `Inner Product` (equivalent to `Cosine Similarity` due to prior normalization).
    *   **IDs**: Generation of stable integer IDs mapped to UUIDs in metadata.
    *   **Metadata**: Stored separately in `metadata.json` (includes original text, source, date).

---

## 3. Retrieval Pipeline (Search)

This flow occurs when the `search_in_faiss` tool is triggered by the Agent.

### Steps:
1.  **Query Embedding**: The user's question is converted into a vector using the same model (`all-MiniLM-L6-v2`) and normalization.
2.  **Vector Search (K-NN)**: Search for the nearest neighbors in FAISS.
    *   **Base Top-K**: `5` results (retrieves the 5 most similar chunks).
3.  **Filtering (Threshold)**: Application of quality cutoff.
    *   **Similarity Threshold**: `0.50` (Results with a score lower than 0.50 are discarded - Updated from 0.35 to reduce noise).
4.  **Formatting**:
    *   **Output**: Structured JSON containing `text`, `score`, `source`, `id`.
    *   **Context**: The Agent receives the JSON and decides how to present the information.

---

## 4. Configuration Reference

| Parameter | Current Value | Definition File | Description |
| :--- | :--- | :--- | :--- |
| **Model Name** | `all-MiniLM-L6-v2` | `model_config.yaml` / `src/rag/embedding.py` | SBERT model for embeddings. |
| **Embedding Dim** | `384` | `src/rag/index.py` | Output vector dimension of the model. |
| **Chunk Size** | `1000` chars | `src/rag/tools.py` | Maximum size of each text chunk. |
| **Chunk Overlap** | `100` chars | `src/rag/tools.py` | Overlap between adjacent chunks. |
| **Top K** | `5` | `src/rag/retriever.py` | Maximum number of returned results. |
| **Threshold** | `0.50` | `src/rag/retriever.py` | Minimum similarity score (Cosine). |
| **Index Type** | `IDMap(FlatIP)` | `src/rag/index.py` | FAISS index architecture (Flat Inner Product). |
| **Normalization** | `True` (L2) | `src/rag/embedding.py` | Ensures `Inner Product` == `Cosine Sim`. |

---

## 5. Execution Lineage (Full Flow)

Describes the exact sequence of events from the user to the RAG response.

1.  **User Input**:
    *   Example: *"Tell me about Book of Roles"*
2.  **Agent (ReAct/LangGraph)**:
    *   Receives input.
    *   Analyzes System Prompt (which instructs to use tools for company facts).
    *   Decides to call Tool: `search_in_faiss(query="Book of Roles")`.
3.  **RAG Tool Execution (`src/rag/tools.py`)**:
    *   Calls `get_retriever()` (Lazy Loading).
4.  **Loader (`src/rag/index.py`)**:
    *   Checks if `metadata.json` exists.
    *   **Fallback**: If not, loads `texts.json` (legacy) and converts in memory.
5.  **Embedder**:
    *   Generates query embedding -> `[0.02, -0.1, ..., 0.5]`.
6.  **FAISS Search**:
    *   Calculates inner product between query and all vectors.
    *   Returns Top-5 IDs and Scores.
7.  **Retriever Filtering (`src/rag/retriever.py`)**:
    *   Checks each returned score `s`.
    *   **Rule**: If `s < 0.50`, discard.
8.  **Output Generation**:
    *   Retriever assembles list of `Document` objects.
    *   Tool converts to JSON: `{"results": [{"text": "...", "score": 0.54}, ...]}`.
9.  **Agent (LLM)**:
    *   Receives JSON as "Observation".
    *   Thinks: *"I found relevant information."*
    *   Generates final response: *"The Book of Roles is..."* (without mentioning internal metadata or explaining it didn't use a tool, if it hadn't).
10. **UI**:
    *   Displays final response to the user.
